//
//	MiscDateView.m -- a view for dealing with input and display of dates
//		Written by Hugh Ashton, Copyright (c) 1994 by Hugh Ashton.
//				Version 1.0  All rights reserved.
//		This notice may not be removed from this source code.
//
//	This object is included in the MiscKit by permission from the author
//	and its use is governed by the MiscKit license, found in the file
//	"LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
//	for a list of all applicable permissions and restrictions.
//	

#include <time.h>
#include <libc.h>
#include <strings.h>
#import "MiscDateView.h"

#define CURRENT_VERSION 1

id	BundleForStrings = nil;

#define MONDAY NXLocalizedStringFromTableInBundle (NULL,BundleForStrings,"Monday",NULL,zeroth day of week)
#define TUESDAY NXLocalizedStringFromTableInBundle (NULL,BundleForStrings,"Tuesday",NULL,first day of week)
#define WEDNESDAY NXLocalizedStringFromTableInBundle (NULL,BundleForStrings,"Wednesday",NULL,second day of week)
#define THURSDAY NXLocalizedStringFromTableInBundle (NULL,BundleForStrings,"Thursday",NULL,third day of week)
#define FRIDAY NXLocalizedStringFromTableInBundle (NULL,BundleForStrings,"Fri",NULL,fourth day of week)
#define SATURDAY NXLocalizedStringFromTableInBundle (NULL,BundleForStrings,"Sat",NULL,fifth day of week)
#define SUNDAY NXLocalizedStringFromTableInBundle (NULL,BundleForStrings,"Sun",NULL,sixth day of week)
#define DATE_ERROR NXLocalizedStringFromTableInBundle (NULL,BundleForStrings,"Date entry error",NULL,error in date entry)

@implementation MiscDateView

+ initialize
{
	if ( self == [MiscDateView class] )
		[MiscDateView setVersion:CURRENT_VERSION];

	return self;
}

- decDay:sender
{
	int mm;
	
	if(strlen([day stringValue])>0)
	{
		[day setIntValue:[day intValue]-1];
	}
	if([day intValue]<1)
	{
		[month setIntValue:[month intValue]-1];
		if([month intValue]<1)
		{
			[month setIntValue:12];
			[year setIntValue:[year intValue]-1];
		}
		mm=[month intValue];
		if(mm==4 || mm==6 || mm==9 || mm==11)
/*
	30 days hath September...
*/		{
			[day setIntValue:30];
			[self outputDOW:[self whatDayIs:[day intValue] :[month intValue] : [year intValue]]];
			return self;
		}
		if(mm==2)
		{
			if([self isItaLeapYear:[year intValue]])
/*
leap year
*/
			{
				[day setIntValue:29];
			}
			else
			{
				[day setIntValue:28];
			}
			[self outputDOW:[self whatDayIs:[day intValue] :[month intValue] : [year intValue]]];
			return self;
		}
		[day setIntValue:31];
	}
	[self outputDOW:[self whatDayIs:[day intValue] :[month intValue] : [year intValue]]];
	[self isDateValid:self];
				
	return self;
}

- decMonth:sender
{
	if(strlen([month stringValue])>0)
	{
		[month setIntValue:[month intValue]-1];
	}
	if([month intValue]<1)
	{
		[month setIntValue:12];
		[year setIntValue:[year intValue]-1];
	}
	[self outputDOW:[self whatDayIs:[day intValue] :[month intValue] : [year intValue]]];
	[self isDateValid:self];
    return self;
}

- outputDOW:(unsigned int)number
{
	number=number%7;
	
	if(number==0)
	{
		[dow setStringValue:MONDAY];
		return self;
	}
	if(number==1)
	{
		[dow setStringValue:TUESDAY];
		return self;
	}
	if(number==2)
	{
		[dow setStringValue:WEDNESDAY];
		return self;
	}
	if(number==3)
	{
		[dow setStringValue:THURSDAY];
		return self;
	}
	if(number==4)
	{
		[dow setStringValue:FRIDAY];
		return self;
	}
	if(number==5)
	{
		[dow setStringValue:SATURDAY];
		return self;
	}
	if(number==6)
	{
		[dow setStringValue:SUNDAY];
		return self;
	}
	return self;
}
	
- decYear:sender
{
	if(strlen([year stringValue])>0)
	{
		[year setIntValue:[year intValue]-1];
	}
	[self outputDOW:[self whatDayIs:[day intValue] :[month intValue] : [year intValue]]];
	[self isDateValid:self];
	return self;
}

- checkDay:sender
{
	if((strlen([day stringValue])==0) || (strlen([month  stringValue])==0) || (strlen([year stringValue])==0))
	{
		[dow setStringValue:"XXX"];
		return self;
	}
	[self outputDOW:[self whatDayIs:[day intValue] :[month intValue] : [year intValue]]];
	[self isDateValid:self];
	return self;
}
- (unsigned int)whatDayIs:(int)theDay :(int)theMonth :(int)theYear
/*
	we take 1 January 1901 as our base
	Mon=0, Tue=1, etc
*/
{
	unsigned int i;
	unsigned int current_count;
	current_count=1;
/*
	as it happens, 1/1/1901 was a Tuesday
*/
	if(theYear<1901)
	{
		return 00;
	}
	for(i=1901;i<theYear;i++)
	{
		if([self isItaLeapYear:i])
		{
			current_count=(current_count+=366);
		}
		else
		{
			current_count=(current_count+=365);
		}
	}
	for(i=1;i<theMonth;i++)
	{
		if((i==4) || (i==6) || (i==9) || (i==11))
		{
			current_count+=30;
		}
		if((i==2) && ([self isItaLeapYear:theYear]))
		{
			current_count+=29;
		}
		if((i==2) && (![self isItaLeapYear:theYear]))
		{
			current_count+=28;
		}
		if((i==1) || (i==3) || (i==5) || (i==7) || (i==8) || (i==10) || (i==12))
		{
			current_count+=31;
		}
	}
	for(i=1;i<theDay;i++)
	{
		current_count+=1;
	}
	
/*
	fencepost error, hence this kludge
	current_count=current_count-1;
*/
	return current_count;
}
- (BOOL)isItaLeapYear:(int)theYear
{
	// Fix from Rod Ragner:
    return ((!(theYear % 4) && (theYear % 200)) ? YES : NO);
// old way is incorrect:
//	if((theYear/4.0)==(theYear/4))
//	{
//		return YES;
//	}
//	else
//	{
//		return NO;
//	}
}
- getTodaysDate:sender
{
	long currenttime;
	char dateString[60]="";
	int dd;
	int mm=0;
	char mmStr[4]="";
	int yyyy;
	char dowStr[4]="";
	char timeString[20]="";

	currenttime = time(0L);
	strcpy(dateString,ctime(&currenttime));
	
	sscanf(dateString,"%s %s %d %s %d",dowStr,mmStr,&dd,timeString,&yyyy);
	[dow setStringValue:dowStr];
	[year setIntValue:yyyy];
	[day setIntValue:dd];
	if(strcmp(mmStr,"Jan")==0)
	{
		mm=1;
	}
	if((strcmp(mmStr,"Feb"))==0)
	{
		mm=2;
	}
	if((strcmp(mmStr,"Mar"))==0)
	{
		mm=3;
	}
	if((strcmp(mmStr,"Apr"))==0)
	{
		mm=4;
	}
	if((strcmp(mmStr,"May"))==0)
	{
		mm=5;
	}
	if((strcmp(mmStr,"Jun"))==0)
	{
		mm=6;
	}
	if((strcmp(mmStr,"Jul"))==0)
	{
		mm=7;
	}
	if((strcmp(mmStr,"Aug"))==0)
	{
		mm=8;
	}
	if((strcmp(mmStr,"Sep"))==0)
	{
		mm=9;
	}
	if((strcmp(mmStr,"Oct"))==0)
	{
		mm=10;
	}
	if((strcmp(mmStr,"Nov"))==0)
	{
		mm=11;
	}
	if((strcmp(mmStr,"Dec"))==0)
	{
		mm=12;
	}
	[month setIntValue:mm];
	return self;
}

- incDay:sender
{
	int mm=0;
	
	mm=[month intValue];
	
	if(strlen([day stringValue])>0)
	{
		[day setIntValue:[day intValue]+1];
	}
	if((mm==4 || mm==6 || mm==9 || mm==11) && [day intValue]>30)
	{
		[month setIntValue:[month intValue]+1];
		[day setIntValue:1];
		[self outputDOW:[self whatDayIs:[day intValue] :[month intValue] : [year intValue]]];
		return self;
	}
	if((mm==2) && ([day intValue]>28) && (![self isItaLeapYear:[year intValue]]))
	{
		[month setIntValue:3];
		[day setIntValue:1];
		[self outputDOW:[self whatDayIs:[day intValue] :[month intValue] : [year intValue]]];
		return self;
	}
	if((mm==2) && ([day intValue]>29) && ([self isItaLeapYear:[year intValue]]))
	{
		[month setIntValue:3];
		[day setIntValue:1];
		[self outputDOW:[self whatDayIs:[day intValue] :[month intValue] : [year intValue]]];
		return self;
	}
	
	if([day intValue]>31)
	{
		[month setIntValue:[month intValue]+1];
		[day setIntValue:1];
		if([month intValue]>12)
		{
			[month setIntValue:1];
			[year setIntValue:[year intValue]+1];
		}
	}
	[self outputDOW:[self whatDayIs:[day intValue] :[month intValue] : [year intValue]]];
	[self isDateValid:self];
	return self;
}

- incMonth:sender
{
	if(strlen([month stringValue])>0)
	{
		[month setIntValue:[month intValue]+1];
	}
	if([month intValue]>12)
	{
		[month setIntValue:1];
		[year setIntValue:[year intValue]+1];
	}
	[self outputDOW:[self whatDayIs:[day intValue] :[month intValue] : [year intValue]]];
	[self isDateValid:self];
	return self;
}

- incYear:sender
{
	if(strlen([year stringValue])>0)
	{
		[year setIntValue:[year intValue]+1];
	}
	[self outputDOW:[self whatDayIs:[day intValue] :[month intValue] : [year intValue]]];
	[self isDateValid:self];
	return self;
}

- (BOOL)isDateValid:sender
{
	if(([year intValue]>2050) || ([year intValue]<1901))
	{
		NXRunAlertPanel(DATE_ERROR,"Year is outside range 1901 to 2050","OK",NULL,NULL);
		[year selectText:self];
		[dow setStringValue:"XXX"];
		return NO;
	}
	if(([month intValue]>12) || ([month intValue]<1))
	{
		NXRunAlertPanel(DATE_ERROR,"Month is outside range 1 to 12","OK",NULL,NULL);
		[month selectText:self];
		[dow setStringValue:"XXX"];
		return NO;
	}
	if(([day intValue]<1) || ([day intValue]>31))
	{
		NXRunAlertPanel(DATE_ERROR,"Day is outside range 1 to 31","OK",NULL,NULL);
		[day selectText:self];
		[dow setStringValue:"XXX"];
		return NO;
	}
	if(([month intValue]==4 || [month intValue]==6 || [month intValue]==9 || [month intValue]==11) && [day intValue]>30)
	{
		NXRunAlertPanel(DATE_ERROR,"Day is outside range for this month (1 to 30)","OK",NULL,NULL);
		[day selectText:self];
		[dow setStringValue:"XXX"];
		return NO;
	}
	if([month intValue]==2 && ![self isItaLeapYear:[year intValue]] && [day intValue]>28)
	{
		NXRunAlertPanel(DATE_ERROR,"Day is outside range for this month (1 to 28). %d is not a leap year","OK",NULL,NULL,[year intValue]);
		[day selectText:self];
		[dow setStringValue:"XXX"];
		return NO;
	}
	if([month intValue]==2 && [self isItaLeapYear:[year intValue]] && [day intValue]>29)
	{
		NXRunAlertPanel(DATE_ERROR,"Day is outside range for this month (1 to 29). %d is a leap year","OK",NULL,NULL,[year intValue]);
		[day selectText:self];
		[dow setStringValue:"XXX"];
		return NO;
	}
	
	
	return YES;
}

- initFrame:(const NXRect *)frameRect
{
	[super initFrame:frameRect];

	return self;
}

- drawSelf:(const NXRect *)rects :(int)rectCount
{
	PSsetgray(NX_LTGRAY);
	NXRectFill(&bounds);
	return self;
}

- read:(NXTypedStream *)stream
{
	int version;
	[super read:stream];
	version = NXTypedStreamClassVersion(stream, "MiscDateView");
	if ( version == CURRENT_VERSION ) {
		day			= NXReadObject(stream);
		dayDown		= NXReadObject(stream);
		dayUp		= NXReadObject(stream);
		dow			= NXReadObject(stream);
		month		= NXReadObject(stream);
		monthDown	= NXReadObject(stream);
		monthUp		= NXReadObject(stream);
		year		= NXReadObject(stream);
		yearDown	= NXReadObject(stream);
		yearUp		= NXReadObject(stream);
	} else {
		[self error:"Unknown version!"];
	}
	return self;
}

- write:(NXTypedStream *)stream
{
	[super write:stream];
	NXWriteObject(stream, day);
	NXWriteObject(stream, dayDown);
	NXWriteObject(stream, dayUp);
	NXWriteObject(stream, dow);
	NXWriteObject(stream, month);
	NXWriteObject(stream, monthDown);
	NXWriteObject(stream, monthUp);
	NXWriteObject(stream, year);
	NXWriteObject(stream, yearDown);
	NXWriteObject(stream, yearUp);
	return self;
}


- (int)getDay:sender
{
	return [day intValue];
}

- (int)getMonth:sender
{
	return [month intValue];
}

- (int)getYear:sender
{
	return [year intValue];
}

- day:sender
{
	return day;
}

- month:sender
{
	return month;
}

- year:sender
{
	return year;
}
@end
